/**
 * Copyright (C)  Gadglet .
 *
 * This file is part of Gadglet
 *
 * Gadglet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Gadglet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Gadglet. If not, see <http://www.gnu.org/licenses/>.
 */


package com.gadglet.data;

import java.util.List;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.gadglet.core.GadgletRequestWrapper;
import com.gadglet.core.RequestException;
import com.gadglet.data.utils.DomainUserStatus;
import com.gadglet.data.utils.PMF;
import com.gadglet.params.ProfileFields;
import com.gadglet.params.SharedConstants;
import com.google.appengine.api.NamespaceManager;
import com.google.appengine.api.oauth.OAuthRequestException;
import com.google.appengine.api.oauth.OAuthService;
import com.google.appengine.api.oauth.OAuthServiceFactory;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;


/**
 * Utility class for DomainUser
 *
 */
public class DomainUserUtils {
	static Logger log = Logger.getLogger("DomainUserUtils");

	/**
	 * Used by the web (not Gadget) to Admin the Gadgets
	 * @return true if the user is the App Engine Admin
	 */
	public static boolean isOpenIdUserAdmin() {
		try {
			UserService userService = UserServiceFactory.getUserService();
			if (userService != null && userService.isUserAdmin())
				return true;
			else
				return false;
		} catch (Exception e) {
			// add log
			return false;
		}
	}

	/**
	 * @return true if the current user is Domain Admin
	 */
	public static boolean isCurrentUserDomainAdmin() {

		DomainUser user = null;
		user = getMyDomainUser();

		if (user == null)
			return false;

		return user.isDomainAdmin();

	}
	
	
	/**
	 * List all my Domain Users 
	 * @param user - current user
	 * @param orderBy - order by field (default: nickName ascending)
	 * @param startFrom - first user index (for paging)
	 * @param itemNum - number of users to return 
	 * @return List all my Domain Users 
	 */
	public static List <DomainUser> getMyDomainUsers(DomainUser user,String orderBy, long startFrom,  long itemNum){
		
		String nameSpace = NamespaceManager.get();
		NamespaceManager.set("");
	
		List <DomainUser> domainUser = null;

	
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(DomainUser.class);
		

		try {
			if(orderBy !=null)
				query.setOrdering(orderBy);
			else
				query.setOrdering("nickName ascending");
			
			if(startFrom>0 && itemNum > 0){
				query.setRange(startFrom, startFrom+itemNum);
			}
			
			
			query.setFilter("accountId == accountIdParam ");
			query.declareParameters("String accountIdParam");
			pm.currentTransaction().begin();
			domainUser = (List <DomainUser>) query.execute(user.getAccount());
		

		} catch (Exception e) {
			log.warning(e.getMessage());
		} finally {
		
			if (pm.currentTransaction().isActive())
				pm.currentTransaction().rollback();
			
			pm.close();
			
			NamespaceManager.set(nameSpace);

		}
	
		NamespaceManager.set(nameSpace);
		
		return domainUser;
		
	}
	
	/**
	 * Provide access to DomainUser from the gadgets. Namesapce
	 * is disable and reassign before exit. 
	 * 
	 * @param uniqueId - requested user uniqueId
	 * @param myDomainId - current user domain id
	 * @return DomainUser
	 */
	public static DomainUser getDomainUserByUniqueId(String uniqueId, String myDomainId){
		
			
		String nameSpace = NamespaceManager.get();
		NamespaceManager.set("");
	

		DomainUser domainUser = null;

	
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(DomainUser.class);
		pm.currentTransaction().begin();

		try {

			query.setFilter("uniqueId == uniqueIddParam ");
			query.declareParameters("String uniqueIddParam");
			query.setUnique(true);
			domainUser = (DomainUser) query.execute(uniqueId);

			// security check .. is requester domain
			if(domainUser!=null && !domainUser.getAccount().equals(myDomainId))
				domainUser = null;

		} catch (Exception e) {
			log.warning(e.getMessage());
		} finally {
			if (pm.currentTransaction().isActive())
				pm.currentTransaction().rollback();
			
			pm.detachCopy(domainUser);
			pm.close();
			
			
			NamespaceManager.set(nameSpace);

		}
	
		NamespaceManager.set(nameSpace);
		
		return domainUser;
		
	}

	/**
	 * Updates current user record with: PROFILE_THUMBNAILURL, PROFILE_TITLE, PROFILE_NICKNAME <br>
	 *  Namesapce is disable and reassign before exit. 
	 * @param request the request from the gadget
	 */
	public static void updateMyUserInfo(GadgletRequestWrapper request) {

		String nameSpace = NamespaceManager.get();
		NamespaceManager.set("");
		DomainUser currUser = request.getCurrentDomainUser();

		DomainUser domainUser = null;

	
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(DomainUser.class);
		pm.currentTransaction().begin();

		try {

			query.setFilter("uniqueId == uniqueIddParam ");
			query.declareParameters("String uniqueIddParam");
			query.setUnique(true);
			domainUser = (DomainUser) query.execute(currUser.getUniqueId());
			
			if (domainUser!=null){
		
				domainUser.setPhotoUrl(request.getParameter(ProfileFields.PROFILE_THUMBNAILURL.getParamName()));
				domainUser.setTitle(request.getParameter(ProfileFields.PROFILE_TITLE.getParamName()));
				domainUser.setNickName(request.getParameter(ProfileFields.PROFILE_NICKNAME.getParamName()));
				
				pm.makePersistent(domainUser);
				
				pm.currentTransaction().commit();
				
				request.getSession().setAttribute("domainUser",
						domainUser);
			}
			
			

		} catch (Exception e) {
			log.warning(e.getMessage());
		} finally {
			if (pm.currentTransaction().isActive())
				pm.currentTransaction().rollback();
			pm.close();
			
			NamespaceManager.set(nameSpace);

		}
	
		NamespaceManager.set(nameSpace);

	}

	
	
	/**
	 * @return DomainUser based on either Oauth or OpenID
	 */
	public static DomainUser getMyDomainUser() {
		DomainUser user = null;

		user = getMyDomainUserWithOauth();
		if (user == null)
			user = getMyDomainUserWithOpenID();

		return user;

	}

	/**
	 * @return DomainUser based on OAuth
	 */
	public static DomainUser getMyDomainUserWithOauth() {

		DomainUser domainUser = null;
		User user = null;
		try {
			OAuthService oauth = OAuthServiceFactory.getOAuthService();
			if (oauth != null)
				user = oauth.getCurrentUser();

		} catch (OAuthRequestException e) {
			log.info(e.getMessage());

		}

		if (user != null)
			domainUser = getDomainUserByOauth(user);

		return domainUser;
	}

	/**
	 * @return DomainUser based on OpenID
	 */
	public static DomainUser getMyDomainUserWithOpenID() {
		UserService userService = UserServiceFactory.getUserService();
		User user = userService.getCurrentUser();

		DomainUser domainUser = null;
		// TODO: define a table with supported platforms
		if (user != null)
			domainUser = getDomainUserByOpenId(user);

		return domainUser;
	}

	/**
	 * @param user
	 * @return
	 */
	private static DomainUser getDomainUserByOauth(User user) {
		// security need to be current or admin

		DomainUser domainUser = null;

		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(DomainUser.class);
		pm.currentTransaction().begin();

		try {

			query.setFilter("userId == userIdParam && oAuthDomain==authDomainParam");
			query.declareParameters("String userIdParam, String authDomainParam");
			query.setUnique(true);
			domainUser = (DomainUser) query.execute(user.getUserId(),
					user.getAuthDomain());

		} catch (Exception e) {
			log.warning(e.getMessage());
		} finally {
			if (pm.currentTransaction().isActive())
				pm.currentTransaction().rollback();
			pm.close();

		}
		return domainUser;
	}

	/**
	 * @param user
	 * @return
	 */
	private static DomainUser getDomainUserByOpenId(User user) {
		// security need to be current or admin

		DomainUser domainUser = null;

		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(DomainUser.class);
		pm.currentTransaction().begin();
		try {

			query.setFilter("userId == userIdParam && openIdAuthDomain==authDomainParam");
			query.declareParameters("String userIdParam, String authDomainParam");
			query.setUnique(true);
			domainUser = (DomainUser) query.execute(user.getUserId(),
					user.getAuthDomain());

		} catch (Exception e) {
			log.warning(e.getMessage());
		} finally {
			if (pm.currentTransaction().isActive())
				pm.currentTransaction().rollback();
			pm.close();

		}
		return domainUser;
	}

	/**
	 * @param request
	 * @return DomainUser based on OpenSocial(opensocial_viewer_id, oauth_consumer_key)
	 */
	public static DomainUser getDomainUserByOpenSocial(
			GadgletRequestWrapper request) {
		// security need to be current or admin

		String viewrId = request.getOpenSocialViewerId();
		String consumerKey = request.getOpenSocialConsumerKey();

		DomainUser user = null;

		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(DomainUser.class);
		pm.currentTransaction().begin();
		try {

			query.setFilter("openSocialViewerId == userIdParam && oauthConsumerKey==consumerKey");
			query.declareParameters("String userIdParam, String consumerKey");
			query.setUnique(true);
			user = (DomainUser) query.execute(viewrId, consumerKey);

		} catch (Exception e) {
			log.warning(e.getMessage());
		} finally {
			if (pm.currentTransaction().isActive())
				pm.currentTransaction().rollback();

			pm.close();

		}
		return user;
	}

	/**
	 * @param openIdUser
	 * @param account
	 * @param timeZone
	 * @param regToken
	 * @param registrationMethod
	 * @return
	 * @throws RequestException
	 */
	public static DomainUser addDomainUserUsingOpenId(User openIdUser,
			DomainAccount account, String timeZone, RegistrationToken regToken,
			String registrationMethod) throws RequestException {

		String oAuthDomain = null;

		PersistenceManager pmToken = PMF.get().getPersistenceManager();
		PersistenceManager pmUser = PMF.get().getPersistenceManager();

		if (registrationMethod
				.equalsIgnoreCase(SharedConstants.registrationMethodDual)
				&& regToken.getOauthUserId() != null
				&& !regToken.getOauthUserId().equals(openIdUser.getUserId())) {
			log.warning("security warning, token with oauthId ="
					+ regToken.getOauthUserId() + "\nwas claimed with openid="
					+ openIdUser.getUserId());

			Query regQry = pmToken.newQuery(RegistrationToken.class);
			regQry.setUnique(true);
			regQry.setFilter("tokenId == code");
			regQry.declareParameters("String code");
			pmToken.currentTransaction().begin();
			try {
				regToken = (RegistrationToken) regQry.execute(regToken
						.getTokenID());
				if (regToken != null)
					pmToken.deletePersistent(regToken);

				pmToken.currentTransaction().commit();
			} catch (Exception e) {
				log.warning(e.getMessage());
			} finally {
				if (pmToken.currentTransaction().isActive())
					pmToken.currentTransaction().rollback();

				pmToken.close();
			}
			throw new RequestException("userChangedIdOnRegistration");
		}

		// check if user exists
		DomainUser newUser = null;
		newUser = getDomainUserByOpenId(openIdUser);
		if (newUser != null)
			return newUser;

		String opensocialViewerId = null;
		String consumerKey = null;

		try {
			if (regToken != null) {
				opensocialViewerId = regToken.getOpenSocialViewerId();
				consumerKey = regToken.getConsumerKey();
				oAuthDomain = regToken.getAuthDomain();
			}

			newUser = new DomainUser(openIdUser.getUserId(),
					account.getAccountId(), openIdUser.getEmail(),
					openIdUser.getNickname(),
					DomainUserStatus.CREATED.getUserStatus(), timeZone, false,
					openIdUser.getAuthDomain(), oAuthDomain,
					opensocialViewerId, consumerKey);
			pmUser.currentTransaction().begin();
			newUser = pmUser.makePersistent(newUser);
			pmUser.currentTransaction().commit();

		} catch (Exception e) {
			log.warning(e.getMessage());
		} finally {

			if (pmUser.currentTransaction().isActive())
				pmUser.currentTransaction().rollback();

			pmUser.close();

		}

		try {

			pmToken.currentTransaction().begin();

			Query regQry = pmToken.newQuery(RegistrationToken.class);
			regQry.setUnique(true);
			regQry.setFilter("tokenId == code");
			regQry.declareParameters("String code");
			regToken = (RegistrationToken) regQry
					.execute(regToken.getTokenID());
			pmToken.deletePersistent(regToken);

			pmToken.currentTransaction().commit();

		} catch (Exception e) {
			log.warning(e.getMessage());
		} finally {
			if (pmToken.currentTransaction().isActive())
				pmToken.currentTransaction().rollback();

			pmToken.close();
		}

		return newUser;

	}

}
